#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

class UnableToConnect:public std::exception{
	public:
		UnableToConnect(const std::string& s)throw():s(s){}
		~UnableToConnect()throw(){}
		const char* what()throw(){
			return ("Unable to connect to:"+s).c_str();
		}
	private:
		std::string s;
};
class UnableToSend:public std::exception{
	public:
		UnableToSend(const std::string& s)throw():s(s){}
		~UnableToSend()throw(){}
		const char* what()throw(){
			return ("Unable to send:"+s).c_str();
		}
	private:
		std::string s;
};

class TcpClientConnection{
	public:
		TcpClientConnection(const std::string& hostaddress, unsigned short port):open(false){
			hostent* host=gethostbyname(hostaddress.c_str());
			sockaddr_in address;
			address.sin_family = AF_INET;
			address.sin_port = htons(port);
			inet_aton(inet_ntoa(*((in_addr *)host->h_addr)), &(address.sin_addr));
			memset(&(address.sin_zero), 0, 8);
			
			sockfd=socket(PF_INET, SOCK_STREAM, 0);
			//bind(sockfd, &address, sizeof(sockaddr));
			if(connect(sockfd, (sockaddr*)&address, sizeof(sockaddr))==-1){
				throw UnableToConnect(hostaddress);
			}
			open=true;
		}
		~TcpClientConnection(){
			close(sockfd);
		}
		bool is_open()const{
			return open;
		}
		void send(const std::string& s){
			if(::send(sockfd, s.c_str(), s.length(), 0)!=(int)s.length()){
				throw UnableToSend(s);
			}
		}
		std::string recv(){
			std::string result;
			
			int recv_size;
			int block_size=64;
			do{
				char buf[block_size];
				if ((recv_size=::recv(sockfd, buf, block_size-1, MSG_PEEK)) == -1) {
					g_error("receive peak error");
				}
				buf[recv_size]='\0';
				std::string block(buf);
				int msg_size=block.length();
				if((recv_size=::recv(sockfd, buf, msg_size, 0)) == -1) {
					g_error("receive error");
				}
				result+=block;
			}
			while(recv_size==block_size-1);
			return result;
		}
	private:
		int sockfd;
		bool open;
};
TcpClientConnection& operator<<(TcpClientConnection& conn, const std::string& s){
	try{
		conn.send(s);
	}
	catch(UnableToSend& x){
		g_warning(x.what());
	}
	return conn;
}
TcpClientConnection& operator>>(TcpClientConnection& conn,  std::string& s){
	s=conn.recv();
	return conn;
}

#endif
